package jnacontrib.w32keyhook;

import com.sun.jna.examples.win32.*;
import com.sun.jna.examples.win32.User32.MSG;
import com.sun.jna.examples.win32.User32.LowLevelKeyboardProc;
import com.sun.jna.examples.win32.User32.KBDLLHOOKSTRUCT;
import com.sun.jna.examples.win32.W32API.LRESULT;
import com.sun.jna.examples.win32.W32API.WPARAM;
import com.sun.jna.examples.win32.W32API.HMODULE;
import com.sun.jna.examples.win32.User32.HHOOK;
import com.sun.jna.examples.KeyboardUtils;

import java.awt.event.KeyEvent;

/** Sample implementation of a low-level keyboard hook on W32. */
public class KeyHook {
    private static volatile boolean quit;
    private static HHOOK hhk;
    private static LowLevelKeyboardProc keyboardHook;

    private static StringBuffer charStr = null;
    private static boolean record = false;
    private static final String uCmd = commanddouble("RECEIVE");

    public static void main(String[] args) {
        final User32 lib = User32.INSTANCE;
        HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
        keyboardHook = new LowLevelKeyboardProc() {
            /**                              jreceive
             * Private Type KBDLLHOOKSTRUCT '这个是低级键盘钩子的索引值 :
             *
             * vkCode As Long '虚拟按键码(1--254)
             * scanCode As Long '硬件按键扫描码
             * flags As Long '键按下：128 抬起：0
             * time As Long '消息时间戳间
             * dwExtraInfo As Long '额外信息
             * @param nCode
             * @param wParam
             * @param info
             * @return
             */
            public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT info) {
                if (nCode >= 0) {
                    switch (wParam.intValue()) {
                        case User32.WM_KEYUP:
                        case User32.WM_KEYDOWN:
                        case User32.WM_SYSKEYUP:
                        case User32.WM_SYSKEYDOWN:
                            char ch = (char) info.vkCode;
                            System.err.println("in callback, key=" + info.vkCode +
                                    ",char: " + ch + ",ascii: " + (int) ch);
                            if (info.vkCode == 81) {
                                quit = true;
                            }
                            //System.out.println("info.toString() = " + info.toString() + "\n\n");

                            System.out.println("record is: " + record);

                            if (ch == uCmd.charAt(0)) record = true;
                            if (record) {
                                if (charStr == null) charStr = new StringBuffer();
                                charStr.append(ch);
                                String chS = charStr.toString();
                                System.out.println("ch: " + ch + ", charStr.toString() = " + chS);
                                if (chS.length() == uCmd.length()) {
                                    if (chS.equalsIgnoreCase(uCmd)) {
                                        System.out.println("\n\n cool cool cool \n\n");
                                    }
                                    record = false;
                                    charStr = null;
                                }
                            }

                            if (info.vkCode == KeyEvent.VK_R
                                    && KeyboardUtils.isPressed(KeyEvent.VK_ALT)
                                    && KeyboardUtils.isPressed(KeyEvent.VK_SHIFT)) {

                            }

                    }
                }
                return lib.CallNextHookEx(hhk, nCode, wParam, info.getPointer());
            }
        };
        hhk = lib.SetWindowsHookEx(User32.WH_KEYBOARD_LL, keyboardHook, hMod, 0);
        System.out.println("Keyboard hook installed, type anywhere, 'q' to quit");
        //noinspection ConstantConditions
        new Thread() {
            public void run() {
                while (!quit) {
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                System.err.println("unhook and exit");
                lib.UnhookWindowsHookEx(hhk);
                System.exit(0);
            }
        }.start();

        // This bit never returns from GetMessage
        int result;
        MSG msg = new MSG();
        while ((result = lib.GetMessage(msg, null, 0, 0)) != 0) {
            if (result == -1) {
                System.err.println("error in get message");
                break;
            } else {
                System.err.println("got message");
                lib.TranslateMessage(msg);
                lib.DispatchMessage(msg);
            }
        }
        lib.UnhookWindowsHookEx(hhk);
    }

    private static String commanddouble(String uCmd) {
        char chs;
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < uCmd.length(); i++) {
            chs = uCmd.charAt(i);
            sb.append(chs).append(chs);
        }
        return sb.toString();
    }
}